﻿using Sidvall.Collections;
using Sidvall.Security;
using Sidvall.WorkItemManager.Data;
using Sidvall.WorkItemManager.Net.Services.V1;
using Sidvall.WorkItemManager.Workflow;
using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.WorkItemManager
{
    public class ServerManager
    {
        #region Public Members

        public ServerContent ServerContent { get; private set; }
        protected System.Collections.Generic.Dictionary<long, WorkCondition> ConditionIndex { get; private set; }
        protected System.Collections.Generic.Dictionary<long, WorkRetryPolicy> RetryPolicyIndex { get; private set; }
        protected System.Collections.Generic.Dictionary<long, WorkItemType> WorkItemTypeIndex { get; private set; }

        #region GetConditionProvider

        public IConditionProvider GetConditionProvider(long conditionId)
        {
            WorkCondition item;

            if (!this.ConditionIndex.TryGetValue(conditionId, out item))
                return null;
            var type = Sidvall.SystemContext.Current.TypeCache.GetType(item.Codename);
            return Sidvall.SystemContext.Current.CodeManager.CreateInstance(type) as IConditionProvider;
        }

        #endregion
        #region GetRetryPolicyProvider

        public IRetryPolicyProvider GetRetryPolicyProvider(long retryPolicyId)
        {
            WorkRetryPolicy item;

            if (!this.RetryPolicyIndex.TryGetValue(retryPolicyId, out item))
                return null;
            var type = Sidvall.SystemContext.Current.TypeCache.GetType(item.Codename);

            return Sidvall.SystemContext.Current.CodeManager.CreateInstance(type) as IRetryPolicyProvider;
        }

        #endregion
        #region GetWorkItemProvider

        public IWorkItemProvider GetWorkItemProvider(IWorkItem workItem)
        {
            WorkItemType workItemType;

            if (workItem.WorkItemTypeId == null)
                return null;
            if (!this.WorkItemTypeIndex.TryGetValue(workItem.WorkItemTypeId.Value, out workItemType))
                return null;
            return Sidvall.WorkItemManager.SystemContext.Current.EntityFacade.WorkItemTypeManager.GetWorkItemProvider(workItemType, workItem);
        }

        #endregion

        #region GetWorkItemConditionContextEditor

        public System.Type GetWorkItemConditionContextEditor(long? conditionId)
        {
            WorkCondition item;

            if (conditionId == null)
                return null;
            if (!this.ConditionIndex.TryGetValue(conditionId.Value, out item))
                return null;
            return Sidvall.SystemContext.Current.TypeCache.GetType(item.ContextEditor);
        }

        #endregion
        #region GetWorkItemContextEditor

        public System.Type GetWorkItemContextEditor(long? workItemTypeId)
        {
            WorkItemType item;

            if (workItemTypeId == null)
                return null;
            if (!this.WorkItemTypeIndex.TryGetValue(workItemTypeId.Value, out item))
                return null;
            return Sidvall.SystemContext.Current.TypeCache.GetType(item.ContextEditor);
        }

        #endregion
        #region GetWorkItemExecutionContextEditor

        public System.Type GetWorkItemExecutionContextEditor(long? workItemTypeId)
        {
            WorkItemType item;

            if (workItemTypeId == null)
                return null;
            if (!this.WorkItemTypeIndex.TryGetValue(workItemTypeId.Value, out item))
                return null;
            return Sidvall.SystemContext.Current.TypeCache.GetType(item.ExecutionContextEditor);
        }

        #endregion
        #region GetWorkItemRetryPolicyContextEditor

        public System.Type GetWorkItemRetryPolicyContextEditor(long? retryPolicyId)
        {
            WorkRetryPolicy item;

            if (retryPolicyId == null)
                return null;
            if (!this.RetryPolicyIndex.TryGetValue(retryPolicyId.Value, out item))
                return null;
            return Sidvall.SystemContext.Current.TypeCache.GetType(item.ContextEditor);
        }

        #endregion

        #region GetWorkItemConditionDescriptionAsync

        public async Task<string> GetWorkItemConditionDescriptionAsync(IWorkItem workItem, IWorkItemWorkCondition workItemCondition)
        {
            IConditionProvider provider;
            WorkCondition condition;

            if (workItemCondition == null)
                return null;
            provider = GetConditionProvider(workItemCondition.WorkConditionId);
            if (provider == null)
            {
                if (this.ConditionIndex.TryGetValue(workItemCondition.WorkConditionId, out condition))
                    return condition.Name;
            }
            provider.Context = workItemCondition.ConditionContext;

            return await provider.GetDescriptionAsync(workItem).ConfigureAwait(false);
        }

        #endregion

        #region InitializeAsync

        public async Task InitializeAsync(int? serverConnectionId)
        {
            System.Collections.Generic.List<System.Threading.Tasks.Task> tasks;
            tasks = new System.Collections.Generic.List<System.Threading.Tasks.Task>();
            tasks.Add(LoadServerContentAsync(serverConnectionId));
            tasks.Add(LoadConditionIndexAsync(serverConnectionId));
            tasks.Add(LoadRetryPolicyIndexAsync(serverConnectionId));
            tasks.Add(LoadWorkItemTypeIndexAsync(serverConnectionId));
            await System.Threading.Tasks.Task.WhenAll(tasks).ConfigureAwait(false);

            await AddConditionAsync(serverConnectionId, "Schedule", "Schedule",
                "Sidvall.WorkItemManager.Workflow.Conditions.ScheduleConditionProvider,Sidvall.WorkItemManager",
                "Sidvall.WorkItemManager.UI.ViewModels.ScheduleViewModel,Sidvall.WorkItemManager.UI").ConfigureAwait(false);
            await AddRetryPolicyAsync(serverConnectionId, "Default", "Default",
                "Sidvall.WorkItemManager.Workflow.RetryPolicies.IntervalRetryPolicyProvider,Sidvall.WorkItemManager",
                "Sidvall.WorkItemManager.UI.ViewModels.IntervalRetryPolicyViewModel,Sidvall.WorkItemManager.UI").ConfigureAwait(false);
            await AddWorkItemTypeAsync(serverConnectionId, "Monitor", "Monitor",
                "Sidvall.WorkItemManager.Workflow.WorkItems.MonitorWorkItemProvider,Sidvall.WorkItemManager",
                "Sidvall.WorkItemManager.UI.ViewModels.MonitorContextViewModel,Sidvall.WorkItemManager.UI",
                "Sidvall.WorkItemManager.UI.ViewModels.MonitorExecutionContextViewModel,Sidvall.WorkItemManager.UI").ConfigureAwait(false);
        }

        #endregion

        #endregion
        #region Private Members

        #region AddConditionAsync

        private async Task AddConditionAsync(int? serverConnectionId, string name, string description, string codeName, string contextEditor)
        {
            Sidvall.Reflection.TypeItem typeItem;
            WorkCondition item;

            typeItem = Sidvall.Reflection.TypeItem.Parse(codeName);
            item = (from o in this.ConditionIndex.Values
                    where o.Codename.Trim().StartsWith(typeItem.TypeName, System.StringComparison.OrdinalIgnoreCase)
                    select o).FirstOrDefault();
            if (item == null)
            {
                item = new WorkCondition()
                {
                    ServerConnectionId = serverConnectionId,
                    Name = name,
                    Description = description,
                    Codename = codeName,
                    ContextEditor = contextEditor,
                };
                item = await item.SaveItemAsync().ConfigureAwait(false);
                this.ConditionIndex.Add(item.WorkConditionId.Value, item);
            }
        }

        #endregion
        #region AddRetryPolicyAsync

        private async Task AddRetryPolicyAsync(int? serverConnectionId, string name, string description, string codeName, string contextEditor)
        {
            Sidvall.Reflection.TypeItem typeItem;
            WorkRetryPolicy item;

            typeItem = Sidvall.Reflection.TypeItem.Parse(codeName);
            item = (from o in this.RetryPolicyIndex.Values
                    where o.Codename.Trim().StartsWith(typeItem.TypeName, System.StringComparison.OrdinalIgnoreCase)
                    select o).FirstOrDefault();
            if (item == null)
            {
                item = new WorkRetryPolicy()
                {
                    ServerConnectionId = serverConnectionId,
                    Name = name,
                    Description = description,
                    Codename = codeName,
                    ContextEditor = contextEditor,
                };
                item = await item.SaveItemAsync().ConfigureAwait(false);
                this.RetryPolicyIndex.Add(item.WorkRetryPolicyId.Value, item);
            }
        }

        #endregion
        #region AddWorkItemTypeAsync

        private async Task AddWorkItemTypeAsync(int? serverConnectionId, string name, string description, string codeName, string contextEditor, string executionContextEditor)
        {
            Sidvall.Reflection.TypeItem typeItem;
            WorkItemType item;

            typeItem = Sidvall.Reflection.TypeItem.Parse(codeName);
            item = (from o in this.WorkItemTypeIndex.Values
                    where o.Codename.Trim().StartsWith(typeItem.TypeName, System.StringComparison.OrdinalIgnoreCase)
                    select o).FirstOrDefault();
            if (item == null)
            {
                item = new WorkItemType()
                {
                    ServerConnectionId = serverConnectionId,
                    Name = name,
                    Description = description,
                    Codename = codeName,
                    ContextEditor = contextEditor,
                    ExecutionContextEditor = executionContextEditor,
                };
                item = await item.SaveItemAsync().ConfigureAwait(false);
                this.WorkItemTypeIndex.Add(item.WorkItemTypeId.Value, item);
            }
        }

        #endregion
        #region LoadConditionIndexAsync

        private async Task LoadConditionIndexAsync(int? serverConnectionId)
        {
            var criteria = new Sidvall.Data.FetchListCriteria<Data.Fields.WorkConditionField, Data.Relations.WorkConditionRelation>();
            ParameterManager.InitializeParameters(criteria, serverConnectionId);
            var items = await Sidvall.WorkItemManager.SystemContext.Current.DataPortal.WorkConditionManager.GetItemsAsync(criteria).ConfigureAwait(false);
            this.ConditionIndex = items.Items.ToDictionary(o => o.WorkConditionId.Value, true);
        }

        #endregion
        #region LoadRetryPolicyIndexAsync

        private async Task LoadRetryPolicyIndexAsync(int? serverConnectionId)
        {
            var criteria = new Sidvall.Data.FetchListCriteria<Data.Fields.WorkRetryPolicyField, Data.Relations.WorkRetryPolicyRelation>();
            ParameterManager.InitializeParameters(criteria, serverConnectionId);
            var items = await Sidvall.WorkItemManager.SystemContext.Current.DataPortal.WorkRetryPolicyManager.GetItemsAsync(criteria).ConfigureAwait(false);
            this.RetryPolicyIndex = items.Items.ToDictionary(o => o.WorkRetryPolicyId.Value, true);
        }

        #endregion
        #region LoadWorkItemTypeIndexAsync

        private async Task LoadWorkItemTypeIndexAsync(int? serverConnectionId)
        {
            var criteria = new Sidvall.Data.FetchListCriteria<Data.Fields.WorkItemTypeField, Data.Relations.WorkItemTypeRelation>();
            ParameterManager.InitializeParameters(criteria, serverConnectionId);
            var items = await Sidvall.WorkItemManager.SystemContext.Current.DataPortal.WorkItemTypeManager.GetItemsAsync(criteria).ConfigureAwait(false);
            this.WorkItemTypeIndex = items.Items.ToDictionary(o => o.WorkItemTypeId.Value, true);
        }

        #endregion
        #region LoadServerContentAsync

        private async Task LoadServerContentAsync(int? serverConnectionId)
        {
            this.ServerContent = await Sidvall.WorkItemManager.SystemContext.Current.GetServerContentAsync(serverConnectionId).ConfigureAwait(false);
        }

        #endregion

        #endregion
        #region Constructors

        public ServerManager()
        {
        }

        #endregion
    }
}
